Author: Dhanush Kobal
Project Name: Regression analysis
We are going to do another data analysis on house price data and see how accurate our analysis will be
Evaluation metric: \(\sqrt{\frac{1}{n} \sum_{i=1}^n (log(y_{obs}+1)-log(y_{pred}+1))^2}\)
We will assume that the train data is a good representative of the test data
Loading required package: ParamHelpers
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Warning message: 'mlr' is in 'maintenance-only' mode since July 2019. Future development will only
happen in 'mlr3' (<https://mlr3.mlr-org.com>). Due to the focus on 'mlr3' there might be uncaught
bugs meanwhile in {mlr} - please consider switching.
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ───────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5 ✓ purrr 0.3.4
✓ tibble 3.1.3 ✓ dplyr 1.0.7
✓ tidyr 1.1.3 ✓ stringr 1.4.0
✓ readr 2.0.0 ✓ forcats 0.5.1
── Conflicts ──────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
Attaching package: ‘FSelectorRcpp’
The following object is masked from ‘package:FSelector’:
relief
We will first see how the data is presented
Rows: 1460 Columns: 81
── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (43): MSZoning, Street, Alley, LotShape, LandContour, Utilities, LotConfig, LandSlope, Neighborhood, C...
dbl (38): Id, MSSubClass, LotFrontage, LotArea, OverallQual, OverallCond, YearBuilt, YearRemodAdd, MasVnrA...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Rows: 1459 Columns: 80
── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (43): MSZoning, Street, Alley, LotShape, LandContour, Utilities, LotConfig, LandSlope, Neighborhood, C...
dbl (37): Id, MSSubClass, LotFrontage, LotArea, OverallQual, OverallCond, YearBuilt, YearRemodAdd, MasVnrA...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Rows: 1459 Columns: 2
── Column specification ──────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl (2): Id, SalePrice
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
[1] 1460
[1] 1459
Warning: It is deprecated to specify `guide = FALSE` to remove a guide. Please use `guide = "none"` instead.

We will look at all the numerical variables to first see if we need to convert them into factors




































A preview of our updated data
Outlier analysis
We will see each numeric variables and see if we need to delete any observation
























We will see how representative the train data is with the test data, so I will create factor that tells us which observation is a test data or train data
Outlier analysis: Lotarea


Outlier analysis: MasVnrArea
Warning: Removed 23 rows containing missing values (geom_point).

[1] 298
Warning: Removed 23 rows containing missing values (geom_point).

Outlier analysis: BsmtFinSF1
Warning: Removed 1 rows containing missing values (geom_point).

[1] 1299
Warning: Removed 1 rows containing missing values (geom_point).

Outlier analysis: X3SsnPorch
We removed 1 outlier

[1] 206

The rest of the data seems like a good representative of the test data
We are first going impute the dataset to do further analysis.
Data imputation
We will impute the numerical variables using the Mode and we will impute the categorical variable with a ML Algorithm (naiveBayes)
Now it is important for us to see if the levels in the test and train data are the same.
Now we will check how well the imputation went









































































The imputed values seems reasonable. Yes this does produce a bias in our model, however, this bias might lead to better performance
Feature engineering
Feature engineering: Street

Feature engineering: LotShape
ggplot(data = combined.data[train.kaggle$Id, ] , aes(x = LotShape, y = OverallQual)) + geom_bar(stat = "identity")

a<-ifelse(combined.data$LotShape=="Reg" , "good.overallQual",
ifelse(combined.data$LotShape %in% c("IR1") , "medium.overallQual" , "bad.OverallQual"))
combined.data$LotArea.OverallQual.factor<-as.factor(a)
Feature engineering: Neighborhood

Feature engineering: OverQuall
Feature engineering: OverCond

Feature engineering: TotRmsAbvGrd
Quantitative feature engineering
TSNE
[1] -31.35391 19.75975 -23.86412 -28.19424 -10.59565 -10.24031
Other numerical factors that might be useful
Correlation Plot

We will see if are any data imbalances
$MSSubClass
x
20 30 40 45 50 60 70 75 80 85 90 120 150 160 180 190
0.37 0.05 0.00 0.01 0.10 0.20 0.04 0.01 0.04 0.01 0.04 0.06 0.00 0.04 0.01 0.02
$MSZoning
x
C (all) FV RH RL RM
0.01 0.04 0.01 0.79 0.15
$Street
x
Grvl Pave
0 1
$LotShape
x
IR1 IR2 IR3 Reg
0.33 0.03 0.01 0.64
$LandContour
x
Bnk HLS Low Lvl
0.04 0.03 0.02 0.90
$Utilities
x
AllPub NoSeWa
1 0
$LotConfig
x
Corner CulDSac FR2 FR3 Inside
0.18 0.06 0.03 0.00 0.72
$LandSlope
x
Gtl Mod Sev
0.95 0.04 0.01
$Neighborhood
x
Blmngtn Blueste BrDale BrkSide ClearCr CollgCr Crawfor Edwards Gilbert IDOTRR MeadowV Mitchel NAmes
0.01 0.00 0.01 0.04 0.02 0.10 0.04 0.07 0.05 0.03 0.01 0.03 0.15
NoRidge NPkVill NridgHt NWAmes OldTown Sawyer SawyerW Somerst StoneBr SWISU Timber Veenker
0.03 0.01 0.05 0.05 0.08 0.05 0.04 0.06 0.02 0.02 0.02 0.01
$Condition1
x
Artery Feedr Norm PosA PosN RRAe RRAn RRNe RRNn
0.03 0.06 0.86 0.01 0.01 0.01 0.02 0.00 0.00
$Condition2
x
Artery Feedr Norm PosA PosN RRAe RRAn RRNn
0.00 0.00 0.99 0.00 0.00 0.00 0.00 0.00
$BldgType
x
1Fam 2fmCon Duplex Twnhs TwnhsE
0.84 0.02 0.04 0.03 0.08
$HouseStyle
x
1.5Fin 1.5Unf 1Story 2.5Fin 2.5Unf 2Story SFoyer SLvl
0.10 0.01 0.50 0.01 0.01 0.30 0.03 0.04
$OverallQual
x
1 2 3 4 5 6 7 8 9 10
0.00 0.00 0.01 0.08 0.27 0.26 0.22 0.12 0.03 0.01
$OverallCond
x
1 2 3 4 5 6 7 8 9
0.00 0.00 0.02 0.04 0.56 0.17 0.14 0.05 0.02
$RoofStyle
x
Flat Gable Gambrel Hip Mansard Shed
0.01 0.78 0.01 0.19 0.00 0.00
$Exterior1st
x
AsbShng AsphShn BrkComm BrkFace CBlock CemntBd HdBoard ImStucc MetalSd Plywood Stone Stucco VinylSd
0.01 0.00 0.00 0.03 0.00 0.04 0.15 0.00 0.15 0.07 0.00 0.02 0.35
Wd Sdng WdShing
0.14 0.02
$Exterior2nd
x
AsbShng AsphShn Brk Cmn BrkFace CBlock CmentBd HdBoard ImStucc MetalSd Other Plywood Stone Stucco
0.01 0.00 0.00 0.02 0.00 0.04 0.14 0.01 0.15 0.00 0.10 0.00 0.02
VinylSd Wd Sdng Wd Shng
0.35 0.14 0.03
$MasVnrType
x
BrkCmn BrkFace None Stone
0.01 0.31 0.59 0.09
$ExterQual
x
Ex Fa Gd TA
0.04 0.01 0.33 0.62
$ExterCond
x
Ex Fa Gd Po TA
0.00 0.02 0.10 0.00 0.88
$Foundation
x
BrkTil CBlock PConc Slab Stone Wood
0.10 0.43 0.44 0.02 0.00 0.00
$BsmtQual
x
Ex Fa Gd TA
0.08 0.03 0.42 0.47
$BsmtCond
x
Fa Gd Po TA
0.04 0.04 0.00 0.91
$BsmtExposure
x
Av Gd Mn No
0.15 0.09 0.08 0.68
$BsmtFinType1
x
ALQ BLQ GLQ LwQ Rec Unf
0.15 0.10 0.29 0.06 0.10 0.30
$BsmtFinType2
x
ALQ BLQ GLQ LwQ Rec Unf
0.01 0.02 0.01 0.03 0.04 0.88
$Heating
x
Floor GasA GasW Grav OthW Wall
0.00 0.98 0.01 0.00 0.00 0.00
$HeatingQC
x
Ex Fa Gd Po TA
0.51 0.03 0.17 0.00 0.29
$CentralAir
x
N Y
0.07 0.93
$Electrical
x
FuseA FuseF FuseP Mix SBrkr
0.06 0.02 0.00 0.00 0.91
$BsmtFullBath
x
0 1 2 3
0.59 0.40 0.01 0.00
$BsmtHalfBath
x
0 1 2
0.94 0.05 0.00
$HalfBath
x
0 1 2
0.62 0.37 0.01
$BedroomAbvGr
x
0 1 2 3 4 5 6 8
0.00 0.03 0.25 0.55 0.15 0.01 0.00 0.00
$KitchenAbvGr
x
0 1 2 3
0.00 0.95 0.04 0.00
$KitchenQual
x
Ex Fa Gd TA
0.07 0.03 0.40 0.50
$TotRmsAbvGrd
x
2 3 4 5 6 7 8 9 10 11 12 13 14 15
0.00 0.01 0.07 0.19 0.28 0.22 0.13 0.05 0.03 0.01 0.01 0.00 0.00 0.00
$Functional
x
Maj1 Maj2 Min1 Min2 Mod Sev Typ
0.01 0.00 0.02 0.02 0.01 0.00 0.93
$Fireplaces
x
0 1 2 3 4
0.47 0.45 0.08 0.00 0.00
$GarageType
x
2Types Attchd Basment BuiltIn CarPort Detchd
0.01 0.60 0.01 0.06 0.01 0.31
$GarageFinish
x
Fin RFn Unf
0.24 0.29 0.47
$GarageCars
x
0 1 2 3 4 5
0.06 0.25 0.56 0.12 0.00 0.00
$GarageQual
x
Ex Fa Gd Po TA
0.00 0.07 0.01 0.00 0.92
$GarageCond
x
Ex Fa Gd Po TA
0.00 0.06 0.01 0.01 0.93
$PavedDrive
x
N P Y
0.06 0.02 0.92
$MoSold
x
1 2 3 4 5 6 7 8 9 10 11 12
0.04 0.04 0.07 0.10 0.14 0.17 0.16 0.08 0.04 0.06 0.05 0.04
$SaleType
x
COD Con ConLD ConLI ConLw CWD New Oth WD
0.03 0.00 0.01 0.00 0.00 0.00 0.08 0.00 0.87
$SaleCondition
x
Abnorml AdjLand Alloca Family Normal Partial
0.07 0.00 0.01 0.01 0.82 0.09
$What.kind.of.data
x
test.data train.data
0 1
$Street.factor
x
bad.Street good.Street
0 1
$LotArea.OverallQual.factor
x
bad.OverallQual good.overallQual medium.overallQual
0.03 0.64 0.33
$Overallqual.neigh
x
high.overall.neigh low.overallqual.neigh med.overallqual.neigh
0.20 0.04 0.76
$OverallQual.salecond
x
high.overall.saleprice low.overall.saleprice medium.overall.saleprice
0.16 0.02 0.83
$OverallCond.overallqual
x
high.overall.qual low.overall.qual med.overall.qual
0.58 0.06 0.36
$TotRmsAbvGrd.saleprice
x
high.sale.totrms low.sale.totrms med.sale.totrms
0.05 0.54 0.40
$YearBuilt.as.factor
x
1 2 3 4 5 6 7
0.00 0.02 0.09 0.12 0.29 0.23 0.25
$YearRemodAdd.as.factor
x
1 2 3 4
0.25 0.25 0.25 0.25
$total_bsmt_bath
x
1.5 2 2.5 3 3.5 4.5
0.54 0.05 0.40 0.01 0.01 0.00
Split the data into train and test and feature importances
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio

We will create various transformations for each training and test data
We will create a lasso model for feature selection
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Resampling: cross-validation
Measures: rmsle
[Resample] iter 1: 0.1122575
[Resample] iter 2: 0.1371049
[Resample] iter 3: 0.1304579
Aggregated Result: rmsle.test.mean=0.1266068

We will also create a bagging elastic-net model we well
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Resampling: cross-validation
Measures: rmsle
[Resample] iter 1: 0.1446785
[Resample] iter 2: 0.1231925
[Resample] iter 3: 0.1194127
Aggregated Result: rmsle.test.mean=0.1290946
We will also create a SVM we well
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
Resampling: cross-validation
Measures: rmsle
[Resample] iter 1: 0.1456177
[Resample] iter 2: 0.1260868
[Resample] iter 3: 0.1120551
Aggregated Result: rmsle.test.mean=0.1279199

We will also create a SVM 2 we well. The warning can be safely ignored
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Resampling: cross-validation
Measures: rmsle
[Resample] iter 1: 0.1442733
[Resample] iter 2: 0.1266494
[Resample] iter 3: 0.1101696
Aggregated Result: rmsle.test.mean=0.1270308
Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
Variable(s) ‘X3SsnPorch’ and ‘PoolArea’ constant. Cannot scale data.
Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
Variable(s) ‘PoolArea’ constant. Cannot scale data.
Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
Variable(s) ‘PoolArea’ constant. Cannot scale data.
Warning in svm.default(x, y, scale = scale, ..., na.action = na.action) :
Variable(s) ‘PoolArea’ constant. Cannot scale data.

We will also create a KSVM we well
Attaching package: ‘kernlab’
The following object is masked from ‘package:purrr’:
cross
The following object is masked from ‘package:ggplot2’:
alpha
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
Resampling: cross-validation
Measures: rmsle
[Resample] iter 1: 0.1323306
[Resample] iter 2: 0.1286154
[Resample] iter 3: 0.1267248
Aggregated Result: rmsle.test.mean=0.1292236

We will also create a XGB we well
Resampling: cross-validation
Measures: rmsle.test
[Resample] iter 1: 0.2016001
[Resample] iter 2: 0.2005690
[Resample] iter 3: 0.2102933
Aggregated Result: rmsle.test.mean=0.2041542

[1] 0.1984432
GLMBOOST
Resampling: cross-validation
Measures: rmsle.test
[Resample] iter 1: 0.1526286
[Resample] iter 2: 0.1460310
[Resample] iter 3: 0.1697131
[Resample] iter 4: 0.1452754
[Resample] iter 5: 0.1594117
Aggregated Result: rmsle.test.mean=0.1546120

[1] 0.149486
We will also create a SVM 3 we well
Stacking: Lasso Model
We will create a LASSO model as our superlearner
The base learners were svm, and ksvm and I used a lasso model as our super-learner
Resampling: cross-validation
Measures: rmsle.test
[Resample] iter 1: 0.0719159
[Resample] iter 2: 0.0263779
[Resample] iter 3: 0.0563136
[Resample] iter 4: 0.0316589
[Resample] iter 5: 0.0419256
Aggregated Result: rmsle.test.mean=0.0456384
stacked.data.train<-add_column(stacked.data.train , stacked.model = bb.train.stacked)
stacked.data.test<-add_column(stacked.data.test , stacked.model = bb.test.stacked)
Stacking: Benchmarking
set.seed(2)
svm.reg.scale<-makeLearner("regr.svm" , par.vals = list(nu = 0.5, cost = 3 , tolerance = 0.001), id = "svm.scale")
ksvm.reg<-makeLearner("regr.ksvm", par.vals = list(C = 2, epsilon = 0.001, nu = 0.2), id = "ksvm")
svm.reg.2<-makeLearner("regr.svm", par.vals = list(cost = 4, tolerance = 0.005 , nu = 0.5, scale = TRUE) ,
id = "svm.reg2")
svm.reg<-makeLearner("regr.svm", par.vals = list(cost = 4 , nu = 0.5 ,tolerance = 0.001 ), id = "svm")
lrns = list(svm.reg, svm.reg.2, svm.reg.scale)
train.task<-makeRegrTask(data = train.data %>% select(feat), target = "SalePrice")
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
repcv = makeResampleDesc("RepCV" , folds = 3, rep = 5)
b1<-benchmark(lrns, train.task, repcv, models = TRUE , measures = rmsle, show.info = FALSE)
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,GarageCars,TotRmsAbvGrd,Fireplaces
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Warning in makeTask(type = type, data = data, weights = weights, blocking = blocking, :
Empty factor levels were dropped for columns: MSSubClass,TotRmsAbvGrd,Fireplaces,GarageCars
Stacking: Benchmarking boxplots
We see that benchmarking averaged the results of all the ML models
We can create another stacking model to increase performance, or we can manually adjust the weights to see how well we do it.
No id variables; using all as measure variables

Stacking: Manuel weights based on RMSLE
It is very evident that the custom weights we have created gave us the greatest overall performance. We will use these custom weights for represent our test data as well.
No id variables; using all as measure variables

The final predictions
z<-stacked.data.test %>% select(c("svm","ksvm", "glm.boost", "stacked.model"))
a<-0.6
b<-0
c<-0.2
d<-0.7
pp<-apply(z , 1, function(x){return(weighted.mean(x, w = c(a,b,c,d)))})
print(head(pp))
1 2 3 4 5 6
122418.3 157235.5 192111.2 195803.6 186949.0 173801.4
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKPHA+KioqQXV0aG9yOiBEaGFudXNoIEtvYmFsKioqIDwvcD4KPHA+KioqUHJvamVjdCBOYW1lOiBSZWdyZXNzaW9uIGFuYWx5c2lzKioqPC9wPgo8cD5XZSBhcmUgZ29pbmcgdG8gZG8gYW5vdGhlciBkYXRhIGFuYWx5c2lzIG9uIGhvdXNlIHByaWNlIGRhdGEgYW5kIHNlZSBob3cgYWNjdXJhdGUgb3VyIGFuYWx5c2lzIHdpbGwgYmU8L3A+Cgo8cD4qKipFdmFsdWF0aW9uIG1ldHJpYzoqKiogJFxzcXJ0e1xmcmFjezF9e259IFxzdW1fe2k9MX1ebiAobG9nKHlfe29ic30rMSktbG9nKHlfe3ByZWR9KzEpKV4yfSQ8L3A+CjxwPldlIHdpbGwgYXNzdW1lIHRoYXQgdGhlICoqKnRyYWluIGRhdGEqKiogaXMgYSBnb29kIHJlcHJlc2VudGF0aXZlIG9mIHRoZSAqKip0ZXN0IGRhdGEqKio8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQpybShsaXN0PWxzKCkpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkobWxyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KHBhcmFsbGVsTWFwKQpsaWJyYXJ5KEZTZWxlY3RvcikKbGlicmFyeShGU2VsZWN0b3JSY3BwKQpgYGAKCjxwPldlIHdpbGwgZmlyc3Qgc2VlIGhvdyB0aGUgZGF0YSBpcyBwcmVzZW50ZWQ8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQojIHVwbG9hZGluZyBhbmQgY29tYmluaW5nIGRhdGEKCmxpYnJhcnkocmVhZHIpCnRyYWluLmthZ2dsZSA8LSByZWFkX2NzdigiL1VzZXJzL2RoYW51c2hrb2JhbC9EZXNrdG9wLzIwMjFcIFByb2plY3RzL0thZ2dsZS9Ib3VzZVwgcHJpY2VcIHByZWRpY3Rpb24vdHJhaW4uY3N2IikKdGVzdC5rYWdnbGUgPC0gcmVhZF9jc3YoIi9Vc2Vycy9kaGFudXNoa29iYWwvRGVza3RvcC8yMDIxXCBQcm9qZWN0cy9LYWdnbGUvSG91c2VcIHByaWNlXCBwcmVkaWN0aW9uL3Rlc3QuY3N2IikKa2V5LnZhbHVlczwtIHJlYWRfY3N2KCIvVXNlcnMvZGhhbnVzaGtvYmFsL0Rlc2t0b3AvMjAyMVwgUHJvamVjdHMvS2FnZ2xlL0hvdXNlXCBwcmljZVwgcHJlZGljdGlvbi9zdWIuY3N2IikKdHJhaW4ua2FnZ2xlPC1hc190aWJibGUodHJhaW4ua2FnZ2xlKQp0ZXN0LmthZ2dsZTwtYXNfdGliYmxlKHRlc3Qua2FnZ2xlKQpucm93KHRyYWluLmthZ2dsZSk7bnJvdyh0ZXN0LmthZ2dsZSkKYW5zd2Vyczwta2V5LnZhbHVlcyRTYWxlUHJpY2UKY29tYmluZWQuZGF0YTwtbXV0YXRlKGJpbmRfcm93cyh0cmFpbi5rYWdnbGUgLCB0ZXN0LmthZ2dsZSkpICMgY29tYmluaW5nIHRyYWluIGFuZCB0ZXN0IGRhdGEKaGVhZChjb21iaW5lZC5kYXRhKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQojIHJlbmFtaW5nIHZhcmlhYmxlcwoKY29sbmFtZXMoY29tYmluZWQuZGF0YSlbY29sbmFtZXMoY29tYmluZWQuZGF0YSkgJWluJSBjKCIxc3RGbHJTRiIgLCAiMm5kRmxyU0YiICwgIjNTc25Qb3JjaCIpXTwtYygiWDFzdEZsclNGIiwgICJYMm5kRmxyU0YiICwgIlgzU3NuUG9yY2giKQoKY29sbmFtZXModHJhaW4ua2FnZ2xlKVtjb2xuYW1lcyh0cmFpbi5rYWdnbGUpICVpbiUgYygiMXN0RmxyU0YiICwgIjJuZEZsclNGIiAsICIzU3NuUG9yY2giKV08LWMoIlgxc3RGbHJTRiIsICAiWDJuZEZsclNGIiAsICJYM1NzblBvcmNoIikKCmNvbG5hbWVzKHRlc3Qua2FnZ2xlKVtjb2xuYW1lcyh0ZXN0LmthZ2dsZSkgJWluJSBjKCIxc3RGbHJTRiIgLCAiMm5kRmxyU0YiICwgIjNTc25Qb3JjaCIpXTwtYygiWDFzdEZsclNGIiwgICJYMm5kRmxyU0YiICwgIlgzU3NuUG9yY2giKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRX0KIyBwbG90dGluZyBtaXNzaW5nIHZhbHVlcwoKbmFuaWFyOjpnZ19taXNzX3Zhcihjb21iaW5lZC5kYXRhICU+JSBkcGx5cjo6c2VsZWN0KC1TYWxlUHJpY2UpKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT02KSkKCmNvbWJpbmVkLmRhdGE8LWNvbWJpbmVkLmRhdGEgJT4lIGRwbHlyOjpzZWxlY3QoLWMoTG90RnJvbnRhZ2UgLEFsbGV5LCBGaXJlcGxhY2VRdSwgUG9vbFFDLEZlbmNlLCBNaXNjRmVhdHVyZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGdWxsQmF0aCkpICMgcmVtb3ZpbmcgdmFyaWFibGVzCgpjb21iaW5lZC5kYXRhPC1jb21iaW5lZC5kYXRhICU+JSBtdXRhdGVfaWYoc2FwcGx5KGNvbWJpbmVkLmRhdGEsIGlzLmNoYXJhY3RlciksIGFzLmZhY3RvcikgCiNjb252ZXJ0aW5nIHRoaW5ncyB0byBmYWN0b3JzCmBgYAoKPHA+V2Ugd2lsbCBsb29rIGF0IGFsbCB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyB0byBmaXJzdCBzZWUgaWYgd2UgbmVlZCB0byBjb252ZXJ0IHRoZW0gaW50byBmYWN0b3JzPC9wPgpgYGB7ciwgZWNobz1GQUxTRSwgcmVzdWx0cz0naGlkZScsZmlnLmtlZXA9J2FsbCd9CmZvcihpIGluIGNvbG5hbWVzKEZpbHRlcihpcy5udW1lcmljLCBjb21iaW5lZC5kYXRhKSkpewogIGhpc3QoY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsXSAlPiUgcHVsbChpKSwgbWFpbj0gaSkKfQpjb21iaW5lZC5kYXRhPC1tdXRhdGVfYXQoY29tYmluZWQuZGF0YSwgLnZhcnMgPSBjKCJNU1N1YkNsYXNzIiwgIk92ZXJhbGxRdWFsIiwgIk92ZXJhbGxDb25kIiwgIkJzbXRGdWxsQmF0aCIsICJCc210SGFsZkJhdGgiLCAiSGFsZkJhdGgiLCAiQmVkcm9vbUFidkdyIiwgIktpdGNoZW5BYnZHciIsICJUb3RSbXNBYnZHcmQiLCAiRmlyZXBsYWNlcyIsICJHYXJhZ2VDYXJzIiwgIk1vU29sZCIpLC5mdW5zID0gYXMuZmFjdG9yKQpgYGAKCi0tLQo8cD5BIHByZXZpZXcgb2Ygb3VyICoqKnVwZGF0ZWQqKiogZGF0YTwvcD4KYGBge3IsIGVjaG89RkFMU0V9CmhlYWQoY29tYmluZWQuZGF0YSwgbj0yKQpgYGAKCi0tLQo8cCBzdHlsZT0iZm9udC1zaXplOjMwcHgiPiBPdXRsaWVyIGFuYWx5c2lzPC9wPgo8cD5XZSB3aWxsIHNlZSBlYWNoIG51bWVyaWMgdmFyaWFibGVzIGFuZCBzZWUgaWYgd2UgbmVlZCB0byBkZWxldGUgYW55IG9ic2VydmF0aW9uPC9wPgoKYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnLGZpZy5rZWVwPSdhbGwnfQpmb3IoaSBpbiBjb2xuYW1lcyhGaWx0ZXIoaXMubnVtZXJpYywgY29tYmluZWQuZGF0YSkpKXsKICBwYXIobWZyb3cgPSBjKDEsMikpCiAgaGlzdChjb21iaW5lZC5kYXRhW3RyYWluLmthZ2dsZSRJZCxdICU+JSBwdWxsKGkpLCBtYWluPSBpKQogIHBsb3QoY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsXSAlPiUgcHVsbChpKSAsIHkgPSBjb21iaW5lZC5kYXRhW3RyYWluLmthZ2dsZSRJZCxdJFNhbGVQcmljZSwgCiAgICAgICAgICAgICBtYWluID0gaSkKfQpgYGAKCjxwPldlIHdpbGwgc2VlIGhvdyByZXByZXNlbnRhdGl2ZSB0aGUgKioqdHJhaW4gZGF0YSoqKiBpcyB3aXRoIHRoZSAqKip0ZXN0IGRhdGEqKiosIHNvIEkgd2lsbCBjcmVhdGUgICoqKmZhY3RvcioqKiB0aGF0IHRlbGxzIHVzIHdoaWNoIG9ic2VydmF0aW9uIGlzIGEgICoqKnRlc3QgZGF0YSBvciB0cmFpbiBkYXRhKioqPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmE8LWlmZWxzZShjb21iaW5lZC5kYXRhJElkICVpbiUgdHJhaW4ua2FnZ2xlJElkICwgInRyYWluLmRhdGEiICwgInRlc3QuZGF0YSIpCmNvbWJpbmVkLmRhdGEkV2hhdC5raW5kLm9mLmRhdGE8LWFzLmZhY3RvcihhKQpgYGAKCgo8cCBzdHlsZT0iZm9udC1zaXplOjIwcHgiPiBPdXRsaWVyIGFuYWx5c2lzOiBMb3RhcmVhPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KYTwtd2hpY2goY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQgLCBdJExvdEFyZWE+MTAwMDAwKQoKZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSksIGFlcyh4PSAxOmxlbmd0aChMb3RBcmVhKSAsIHkgPSBMb3RBcmVhKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIG5vdCByZW1vdmVkIikKCmdncGxvdChkYXRhID0gZmlsdGVyKGNvbWJpbmVkLmRhdGEsICEoSWQgJWluJSBjKGEpKSksIGFlcyh4PSAxOmxlbmd0aChMb3RBcmVhKSAsIHkgPSBMb3RBcmVhKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIHJlbW92ZWQiKQoKY29tYmluZWQuZGF0YTwtZmlsdGVyKGNvbWJpbmVkLmRhdGEsICEoSWQgJWluJSBjKGEpKSkKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjIwcHgiPiBPdXRsaWVyIGFuYWx5c2lzOiBNYXNWbnJBcmVhPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSksIGFlcyh4PSAxOmxlbmd0aChNYXNWbnJBcmVhKSAsIHkgPSBNYXNWbnJBcmVhKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIG5vdCByZW1vdmVkIikKCmE8LWNvbWJpbmVkLmRhdGEkSWRbd2hpY2goY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsIF0kTWFzVm5yQXJlYT4xNTAwKV07YQoKZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSwgIShJZCAlaW4lIGMoYSkpKSwgYWVzKHg9IDE6bGVuZ3RoKE1hc1ZuckFyZWEpICwgeSA9IE1hc1ZuckFyZWEpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAofldoYXQua2luZC5vZi5kYXRhKSArIGdndGl0bGUoIk91dGxpZXIgcmVtb3ZlZCIpCgpjb21iaW5lZC5kYXRhPC1maWx0ZXIoY29tYmluZWQuZGF0YSwgIShJZCAlaW4lIGMoYSkpKQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MjBweCI+IE91dGxpZXIgYW5hbHlzaXM6IEJzbXRGaW5TRjE8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihjb21iaW5lZC5kYXRhKSwgYWVzKHg9IDE6bGVuZ3RoKEJzbXRGaW5TRjEpICwgeSA9IEJzbXRGaW5TRjEpKSArIGdlb21fcG9pbnQoKSArIGZhY2V0X3dyYXAofldoYXQua2luZC5vZi5kYXRhKSArIGdndGl0bGUoIk91dGxpZXIgbm90IHJlbW92ZWQiKQoKYTwtY29tYmluZWQuZGF0YSRJZFt3aGljaChjb21iaW5lZC5kYXRhW3RyYWluLmthZ2dsZSRJZCwgXSRCc210RmluU0YxPjQwMDApXTthCgpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpLCBhZXMoeD0gMTpsZW5ndGgoQnNtdEZpblNGMSkgLCB5ID0gQnNtdEZpblNGMSkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+V2hhdC5raW5kLm9mLmRhdGEpICsgZ2d0aXRsZSgiT3V0bGllciByZW1vdmVkIikKCmNvbWJpbmVkLmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToyMHB4Ij4gT3V0bGllciBhbmFseXNpczogWDNTc25Qb3JjaDwvcD4KPHA+V2UgcmVtb3ZlZCAxIG91dGxpZXI8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KZ2dwbG90KGRhdGEgPSBmaWx0ZXIoY29tYmluZWQuZGF0YSksIGFlcyh4PSAxOmxlbmd0aChYM1NzblBvcmNoKSAsIHkgPSBYM1NzblBvcmNoKSkgKyBnZW9tX3BvaW50KCkgKyBmYWNldF93cmFwKH5XaGF0LmtpbmQub2YuZGF0YSkgKyBnZ3RpdGxlKCJPdXRsaWVyIG5vdCByZW1vdmVkIikKCmE8LWNvbWJpbmVkLmRhdGEkSWRbd2hpY2goY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsIF0kWDNTc25Qb3JjaD41MDApXTthCgpnZ3Bsb3QoZGF0YSA9IGZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpLCBhZXMoeD0gMTpsZW5ndGgoWDNTc25Qb3JjaCkgLCB5ID0gWDNTc25Qb3JjaCkpICsgZ2VvbV9wb2ludCgpICsgZmFjZXRfd3JhcCh+V2hhdC5raW5kLm9mLmRhdGEpICsgZ2d0aXRsZSgiT3V0bGllciByZW1vdmVkIikKCmNvbWJpbmVkLmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCAhKElkICVpbiUgYyhhKSkpCmBgYAoKPHA+VGhlIHJlc3Qgb2YgdGhlIGRhdGEgc2VlbXMgbGlrZSBhIGdvb2QgcmVwcmVzZW50YXRpdmUgb2YgdGhlICoqKnRlc3QgZGF0YSoqKjwvcD4KPHA+V2UgYXJlIGZpcnN0IGdvaW5nIGltcHV0ZSB0aGUgZGF0YXNldCB0byBkbyBmdXJ0aGVyIGFuYWx5c2lzLjwvcD4KLS0tLS0KCjxwIHN0eWxlPSJmb250LXNpemU6MzBweCI+IERhdGEgaW1wdXRhdGlvbjwvcD4KPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBpbXB1dGUgdGhlICoqKm51bWVyaWNhbCoqKiB2YXJpYWJsZXMgdXNpbmcgdGhlICoqKk1vZGUqKiogYW5kIHdlIHdpbGwgaW1wdXRlIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIGEgKioqTUwgQWxnb3JpdGhtIChuYWl2ZUJheWVzKSoqKjwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgxKQpudW08LWltcHV0ZShhcy5kYXRhLmZyYW1lKEZpbHRlcihpcy5udW1lcmljICwgY29tYmluZWQuZGF0YSkpLCBjbGFzc2VzID0gbGlzdChudW1lcmljID0gaW1wdXRlTWVkaWFuKCkpKQpjb21iaW5lZC5kYXRhPC1jb21iaW5lZC5kYXRhICU+JSBzZWxlY3QoLVJvb2ZNYXRsKQp0cmFpbi5rYWdnbGU8LXRyYWluLmthZ2dsZSAlPiUgc2VsZWN0KC1Sb29mTWF0bCkKdGVzdC5rYWdnbGU8LXRlc3Qua2FnZ2xlICU+JSBzZWxlY3QoLVJvb2ZNYXRsKQoKZmFjPC1pbXB1dGUoYXMuZGF0YS5mcmFtZShGaWx0ZXIoaXMuZmFjdG9yICxjb21iaW5lZC5kYXRhKSksIAogICAgICAgICAgICBjbGFzc2VzID0gbGlzdChmYWN0b3IgPSBpbXB1dGVMZWFybmVyKCJjbGFzc2lmLm5haXZlQmF5ZXMiKSkpCgpjb21iaW5lZC5kYXRhPC1iaW5kX2NvbHMobnVtJGRhdGEgLGZhYyRkYXRhICkKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBOb3cgaXQgaXMgaW1wb3J0YW50IGZvciB1cyB0byBzZWUgaWYgdGhlIGxldmVscyBpbiB0aGUgdGVzdCBhbmQgdHJhaW4gZGF0YSBhcmUgdGhlIHNhbWUuPC9wPgoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gTm93IHdlIHdpbGwgY2hlY2sgaG93IHdlbGwgdGhlIGltcHV0YXRpb24gd2VudDwvcD4KYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnLGZpZy5rZWVwPSdhbGwnfQpmb3IoaSBpbiBjb2xuYW1lcyhGaWx0ZXIoaXMubnVtZXJpYywgY29tYmluZWQuZGF0YSkpKXsKICBwYXIobWZyb3cgPSBjKDEsMikpCiAgaGlzdCh0cmFpbi5rYWdnbGUgJT4lIGRwbHlyOjpwdWxsKGkpICwgbWFpbiA9IHBhc3RlKCJOb3QgaW1wdXRlZCIgLCBpKSkKICBoaXN0KGNvbWJpbmVkLmRhdGFbdHJhaW4ua2FnZ2xlJElkLF0gJT4lIGRwbHlyOjpwdWxsKGkpLCBtYWluID0gIkltcHV0ZWQiKQp9CmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9J2hpZGUnLGZpZy5rZWVwPSdhbGwnfQpmb3IgKGkgaW4gY29sbmFtZXMoRmlsdGVyKGlzLmZhY3RvciAsIGNvbWJpbmVkLmRhdGEpKSl7CiAgaWYoaSA9PSAiV2hhdC5raW5kLm9mLmRhdGEiKXticmVha30KICAKICBwYXIobWZyb3cgPSBjKDEsMikpCiAgYmFycGxvdCh0YWJsZSh0cmFpbi5rYWdnbGUgJT4lIGRwbHlyOjpwdWxsKGkpKSwgbWFpbiA9IHBhc3RlKCJOb3QgaW1wdXRlZCIgLCBpKSkKICBiYXJwbG90KHRhYmxlKGNvbWJpbmVkLmRhdGFbdHJhaW4ua2FnZ2xlJElkLF0gJT4lIGRwbHlyOjpwdWxsKGkpKSwgbWFpbiA9ICJpbXB1dGVkIikKICAKfQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFRoZSBpbXB1dGVkIHZhbHVlcyBzZWVtcyByZWFzb25hYmxlLiBZZXMgdGhpcyBkb2VzIHByb2R1Y2UgYSAqKipiaWFzKioqIGluIG91ciBtb2RlbCwgaG93ZXZlciwgdGhpcyBiaWFzIG1pZ2h0IGxlYWQgdG8gYmV0dGVyIHBlcmZvcm1hbmNlPC9wPgoKLS0tLS0KPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzwvcD4KPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogU3RyZWV0PC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmdncGxvdChkYXRhID0gY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsXSAsIGFlcyh4ID0gU3RyZWV0LCB5ID0gU2FsZVByaWNlKSkgKyBnZW9tX2JveHBsb3QoKQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkU3RyZWV0ID09ICJHcnZsIiAsICJiYWQuU3RyZWV0IiAsICJnb29kLlN0cmVldCIpCgpjb21iaW5lZC5kYXRhJFN0cmVldC5mYWN0b3I8LWFzLmZhY3RvcihhKQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IEZlYXR1cmUgZW5naW5lZXJpbmc6IExvdFNoYXBlPC9wPgoKYGBge3J9CmdncGxvdChkYXRhID0gY29tYmluZWQuZGF0YVt0cmFpbi5rYWdnbGUkSWQsIF0gLCBhZXMoeCA9IExvdFNoYXBlLCB5ID0gT3ZlcmFsbFF1YWwpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkTG90U2hhcGU9PSJSZWciICwgImdvb2Qub3ZlcmFsbFF1YWwiLAogICAgICAgICAgaWZlbHNlKGNvbWJpbmVkLmRhdGEkTG90U2hhcGUgJWluJSBjKCJJUjEiKSAsICJtZWRpdW0ub3ZlcmFsbFF1YWwiICwgImJhZC5PdmVyYWxsUXVhbCIpKQoKY29tYmluZWQuZGF0YSRMb3RBcmVhLk92ZXJhbGxRdWFsLmZhY3RvcjwtYXMuZmFjdG9yKGEpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogTmVpZ2hib3Job29kPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmM8LWNvbWJpbmVkLmRhdGEgJT4lIGdyb3VwX2J5KE5laWdoYm9yaG9vZCkgJT4lIHN1bW1hcmlzZShtID0gbWVhbihhcy5udW1lcmljKE92ZXJhbGxRdWFsKSkpCmdncGxvdChkYXRhID0gYyxhZXMoeCA9IE5laWdoYm9yaG9vZCAsIHkgPSBtKSkgKyBnZW9tX3BvaW50KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkKCng8LWMkTmVpZ2hib3Job29kW3doaWNoKGMkbTw9NSldCnk8LWMkTmVpZ2hib3Job29kW3doaWNoKGMkbT41ICYgYyRtPD03KV0KejwtYyROZWlnaGJvcmhvb2Rbd2hpY2goYyRtPjcpXQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkTmVpZ2hib3Job29kICVpbiUgYyhhcy5jaGFyYWN0ZXIoeCkpICwgImxvdy5vdmVyYWxscXVhbC5uZWlnaCIsCiAgICAgICAgICBpZmVsc2UoY29tYmluZWQuZGF0YSROZWlnaGJvcmhvb2QgJWluJSBjKGFzLmNoYXJhY3Rlcih5KSkgLCAibWVkLm92ZXJhbGxxdWFsLm5laWdoIiwgImhpZ2gub3ZlcmFsbC5uZWlnaCIpKQoKY29tYmluZWQuZGF0YSRPdmVyYWxscXVhbC5uZWlnaDwtYXMuZmFjdG9yKGEpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogT3ZlclF1YWxsPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KYTwtaWZlbHNlKGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRPdmVyYWxsUXVhbCk8PTMgLCAibG93Lm92ZXJhbGwuc2FsZXByaWNlIiAsIAogICAgICAgICAgaWZlbHNlKGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRPdmVyYWxsUXVhbCk+MyAmIGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRPdmVyYWxsUXVhbCk8PTcgLCAibWVkaXVtLm92ZXJhbGwuc2FsZXByaWNlIiAsICJoaWdoLm92ZXJhbGwuc2FsZXByaWNlIikpCgpjb21iaW5lZC5kYXRhJE92ZXJhbGxRdWFsLnNhbGVjb25kPC1hcy5mYWN0b3IoYSkKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBGZWF0dXJlIGVuZ2luZWVyaW5nOiBPdmVyQ29uZDwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpjPC1jb21iaW5lZC5kYXRhICU+JSBncm91cF9ieShPdmVyYWxsQ29uZCkgJT4lIHN1bW1hcmlzZShtID0gbWVhbihhcy5udW1lcmljKE92ZXJhbGxRdWFsKSkpCmdncGxvdChkYXRhID0gYyxhZXMoeCA9IE92ZXJhbGxDb25kICwgeSA9IG0pKSArIGdlb21fcG9pbnQoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQoKYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkT3ZlcmFsbENvbmQgJWluJSBjKDE6NCkgLCAibG93Lm92ZXJhbGwucXVhbCIsCiAgICAgICAgICBpZmVsc2UoY29tYmluZWQuZGF0YSRPdmVyYWxsQ29uZCAlaW4lIGMoNSw5KSAsICJoaWdoLm92ZXJhbGwucXVhbCIgLCAibWVkLm92ZXJhbGwucXVhbCIpKQoKY29tYmluZWQuZGF0YSRPdmVyYWxsQ29uZC5vdmVyYWxscXVhbDwtYXMuZmFjdG9yKGEpCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gRmVhdHVyZSBlbmdpbmVlcmluZzogVG90Um1zQWJ2R3JkPC9wPgpgYGB7ciwgZWNobz1GQUxTRX0KYTwtaWZlbHNlKGNvbWJpbmVkLmRhdGEkVG90Um1zQWJ2R3JkICVpbiUgYygyOjYpICwgImxvdy5zYWxlLnRvdHJtcyIsIAogICAgICAgICAgaWZlbHNlKGNvbWJpbmVkLmRhdGEkVG90Um1zQWJ2R3JkICVpbiUgYyg3OjkpICwgIm1lZC5zYWxlLnRvdHJtcyIgLCAiaGlnaC5zYWxlLnRvdHJtcyIpKQoKCmNvbWJpbmVkLmRhdGEkVG90Um1zQWJ2R3JkLnNhbGVwcmljZTwtYXMuZmFjdG9yKGEpCmBgYAoKLS0tLS0KPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gUXVhbnRpdGF0aXZlIGZlYXR1cmUgZW5naW5lZXJpbmc8L3A+Cgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBUU05FPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmxpYnJhcnkoUnRzbmUpCnNldC5zZWVkKDEpClRTTkU8LXNlbGVjdChjb21iaW5lZC5kYXRhICwgLVNhbGVQcmljZSkgJT4lCiAgUnRzbmUocGVycGxleGl0eSA9IDMwKQoKY29tYmluZWQuZGF0YSR0c25lLnkxPC1UU05FJFlbLDFdCmNvbWJpbmVkLmRhdGEkdHNuZS55MjwtVFNORSRZWywyXQoKaGVhZChjb21iaW5lZC5kYXRhJHRzbmUueTEpCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IE90aGVyIG51bWVyaWNhbCBmYWN0b3JzIHRoYXQgbWlnaHQgYmUgdXNlZnVsPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CmNvbWJpbmVkLmRhdGEkWHN0VG90YWxGbG9vcjwtY29tYmluZWQuZGF0YSRYMXN0RmxyU0YgKyBjb21iaW5lZC5kYXRhJFgybmRGbHJTRgpjb21iaW5lZC5kYXRhJEJzbXRGaW5TRjFfMl9hZGQ8LWNvbWJpbmVkLmRhdGEkQnNtdEZpblNGMSArIGNvbWJpbmVkLmRhdGEkQnNtdEZpblNGMgpjb21iaW5lZC5kYXRhJFllYXJCdWlsdC5hcy5mYWN0b3I8LWFzLmZhY3RvcihjdXQoY29tYmluZWQuZGF0YSRZZWFyQnVpbHQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDE4NTAsMjAyNSwgYnkgPSAyNSksIGxhYmVscyA9IGMoMTo3KSkpCmNvbWJpbmVkLmRhdGEkWWVhclJlbW9kQWRkLmFzLmZhY3RvcjwtdW5pcXVlKGN1dChjb21iaW5lZC5kYXRhJFllYXJSZW1vZEFkZCwgc2VxKDE5MjUsIDIwMjUsIGJ5ID0gMjUpLCBsYWJlbHMgPSBjKDE6NCkpKQoKY29tYmluZWQuZGF0YSR0b3RhbF9zZjwtY29tYmluZWQuZGF0YSRCc210RmluU0YxICsgY29tYmluZWQuZGF0YSRCc210RmluU0YyICsgY29tYmluZWQuZGF0YSRYMXN0RmxyU0YrIGNvbWJpbmVkLmRhdGEkWDJuZEZsclNGCgpjb21iaW5lZC5kYXRhJHRvdGFsX2JzbXRfYmF0aDwtIGFzLmZhY3Rvcihhcy5udW1lcmljKGNvbWJpbmVkLmRhdGEkQnNtdEZ1bGxCYXRoKSArIDAuNSAqIGFzLm51bWVyaWMoY29tYmluZWQuZGF0YSRCc210SGFsZkJhdGgpKQoKY29tYmluZWQuZGF0YSR0b3RhbF9wb3JjaDwtY29tYmluZWQuZGF0YSRPcGVuUG9yY2hTRiArIGNvbWJpbmVkLmRhdGEkWDNTc25Qb3JjaCArIGNvbWJpbmVkLmRhdGEkU2NyZWVuUG9yY2ggKyBjb21iaW5lZC5kYXRhJEVuY2xvc2VkUG9yY2gKCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IENvcnJlbGF0aW9uIFBsb3Q8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KY29ycnBsb3Q6OmNvcnJwbG90KGNvcihGaWx0ZXIoaXMubnVtZXJpYywgY29tYmluZWQuZGF0YSkpKQoKYGBgCgotLS0tLQoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBzZWUgaWYgYXJlIGFueSBkYXRhIGltYmFsYW5jZXM8L3A+CmBgYHtyLCBlY2hvPUZBTFNFfQphPC1GaWx0ZXIoaXMuZmFjdG9yLGZpbHRlcihjb21iaW5lZC5kYXRhLCBXaGF0LmtpbmQub2YuZGF0YSA9PSAidHJhaW4uZGF0YSIpKSAlPiUgbWFwKGZ1bmN0aW9uKHgpIHtyb3VuZChwcm9wLnRhYmxlKHRhYmxlKHgpKSwgMil9KQoKYQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW4gYW5kIHRlc3QgYW5kIGZlYXR1cmUgaW1wb3J0YW5jZXM8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KIyBTcGxpdCB0aGUgZGF0YSBpbnRvIHRlc3QgYW5kIHRyYWluIGRhdGEKCnRyYWluLmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCBXaGF0LmtpbmQub2YuZGF0YT09InRyYWluLmRhdGEiKQp0ZXN0LmRhdGE8LWZpbHRlcihjb21iaW5lZC5kYXRhLCBXaGF0LmtpbmQub2YuZGF0YT09InRlc3QuZGF0YSIpCgp0cmFpbi5kYXRhPC0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KC1XaGF0LmtpbmQub2YuZGF0YSkKdGVzdC5kYXRhPC10ZXN0LmRhdGEgJT4lIHNlbGVjdCgtV2hhdC5raW5kLm9mLmRhdGEpCgpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRX0KIyBSRiBpbXBvcnRhbmNlIHBsb3QKc2V0LnNlZWQoMSkKdHJhaW4udGFzay5mZWF0LjE8LW1ha2VSZWdyVGFzayhkYXRhID0gdHJhaW4uZGF0YSwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCmEuZmlsdGVyPC1nZW5lcmF0ZUZpbHRlclZhbHVlc0RhdGEodHJhaW4udGFzay5mZWF0LjEsIG1ldGhvZCA9ICJyYW5kb21Gb3Jlc3RTUkNfaW1wb3J0YW5jZSIpCnBsb3RGaWx0ZXJWYWx1ZXMoYS5maWx0ZXIpICsgdGhlbWVfYncoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkKYGBgCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFdlIHdpbGwgY3JlYXRlIHZhcmlvdXMgKioqdHJhbnNmb3JtYXRpb25zKioqIGZvciBlYWNoIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGE8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KdHJhaW4uZGF0YS5sb2c8LXRyYWluLmRhdGEKCmYubG9nPC1mdW5jdGlvbih4KXtyZXR1cm4obG9nKHgrMSkpfQp0cmFpbi5kYXRhLmxvZzwtdHJhaW4uZGF0YS5sb2cgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLUlkLCAtU2FsZVByaWNlLCAtdHNuZS55MSwgLXRzbmUueTIpLCBmLmxvZykpCnRlc3QuZGF0YS5sb2c8LXRlc3QuZGF0YQp0ZXN0LmRhdGEubG9nPC10ZXN0LmRhdGEubG9nICU+JSBtdXRhdGUoYWNyb3NzKGMod2hlcmUoaXMubnVtZXJpYyksIC1JZCwgLXRzbmUueTEsIC10c25lLnkyKSwgZi5sb2cpKQp0ZXN0LmRhdGEubG9nJFNhbGVQcmljZTwtTkEKCmYuc3FydDwtZnVuY3Rpb24oeCl7cmV0dXJuKHNxcnQoeCkpfQp0cmFpbi5kYXRhLnJvb3Q8LXRyYWluLmRhdGEKdHJhaW4uZGF0YS5yb290PC10cmFpbi5kYXRhLnJvb3QgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLUlkLC1TYWxlUHJpY2UsIC10c25lLnkxLC10c25lLnkyKSwgZi5zcXJ0KSkKCnRlc3QuZGF0YS5yb290PC10ZXN0LmRhdGEKdGVzdC5kYXRhLnJvb3Q8LXRlc3QuZGF0YS5yb290ICU+JSBtdXRhdGUoYWNyb3NzKGMod2hlcmUoaXMubnVtZXJpYyksIC1JZCAsLVNhbGVQcmljZSwgLXRzbmUueTEsIC10c25lLnkyKSwgZi5zcXJ0KSkKdGVzdC5kYXRhLnJvb3QkU2FsZVByaWNlPC1OQQoKCnRyYWluLmRhdGEuc2NhbGU8LXRyYWluLmRhdGEgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLVNhbGVQcmljZSksIHNjYWxlKSkKdGVzdC5kYXRhLnNjYWxlPC10ZXN0LmRhdGEgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLVNhbGVQcmljZSksIHNjYWxlKSkKdHJhaW4uZGF0YS5zY2FsZSRTYWxlUHJpY2U8LXRyYWluLmRhdGEuc2NhbGUkU2FsZVByaWNlLzEwMDAwMDAKdGVzdC5kYXRhLnNjYWxlJFNhbGVQcmljZTwtTkEKCm1pbi5tYXguZnVuY3Rpb248LWZ1bmN0aW9uKHgpe3JldHVybigoeC1taW4oeCkpLyhtYXgoeCktbWluKHgpKSl9CnRyYWluLmRhdGEubWluLm1heDwtdHJhaW4uZGF0YSAlPiUgbXV0YXRlKGFjcm9zcyhjKHdoZXJlKGlzLm51bWVyaWMpLCAtU2FsZVByaWNlKSwgbWluLm1heC5mdW5jdGlvbikpCnRlc3QuZGF0YS5taW4ubWF4PC10ZXN0LmRhdGEgJT4lIG11dGF0ZShhY3Jvc3MoYyh3aGVyZShpcy5udW1lcmljKSwgLVNhbGVQcmljZSksIG1pbi5tYXguZnVuY3Rpb24pKQp0cmFpbi5kYXRhLm1pbi5tYXgkU2FsZVByaWNlPC10cmFpbi5kYXRhLm1pbi5tYXgkU2FsZVByaWNlLzEwMDAwMDAKdGVzdC5kYXRhLm1pbi5tYXgkU2FsZVByaWNlPC1OQQoKIyBzY2FsZXM6OnJlc2NhbGUodGVzdC5kYXRhLCB0bz1jKDAsMSkpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQojIGxlYXJuaW5nIGN1cnZlIGZ1bmN0aW9uCgoKcGxvdC5sZWFybmluZy5jdXJ2ZTwtZnVuY3Rpb24obW9kZWwsIGRhdGEsIGRlc2NyaXB0aW9uKXsKICBDViA9IG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSA1LCBwcmVkaWN0ID0gImJvdGgiKQogIGxjMiA9IGdlbmVyYXRlTGVhcm5pbmdDdXJ2ZURhdGEobGVhcm5lcnMgPSBtb2RlbCAsIHRhc2sgPSBkYXRhLAogIHBlcmNzID0gc2VxKDAuMSwgMSwgYnkgPSAwLjEpLAogIG1lYXN1cmVzID0gbGlzdChybXNsZSwgc2V0QWdncmVnYXRpb24ocm1zbGUsIHRyYWluLm1lYW4pKSwgcmVzYW1wbGluZyA9IENWLAogIHNob3cuaW5mbyA9IEZBTFNFKQogIHByaW50KHBsb3RMZWFybmluZ0N1cnZlKGxjMiwgZmFjZXQgPSAibGVhcm5lciIpICsgdGhlbWVfYncoKSArIGdndGl0bGUobGFiZWwgPSBwYXN0ZShkZXNjcmlwdGlvbikpKQogIHJldHVybihsYzIkZGF0YSkKfQoKYGBgCgoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBjcmVhdGUgYSBsYXNzbyBtb2RlbCBmb3IgKioqZmVhdHVyZSBzZWxlY3Rpb24qKio8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KCmZlYXQ8LWMoKGEuZmlsdGVyJGRhdGEgJT4lIGFycmFuZ2UoZGVzYyh2YWx1ZSkpKSRuYW1lWzE6NzBdLCAiU2FsZVByaWNlIikKc2V0LnNlZWQoMykKbGw8LTkwMApsYXNzby4xIDwtIG1ha2VMZWFybmVyKCJyZWdyLmdsbW5ldCIsIGFscGhhID0gMSwgaWQgPSAibGFzc28iLCBwYXIudmFscyA9IGxpc3Qocz1sbCwgc3RhbmRhcmRpemUgPVRSVUUpKQp0cmFpbi50YXNrLmxhc3NvLmZlYXQuc2VsPC1tYWtlUmVnclRhc2soZGF0YSA9IHRyYWluLmRhdGEgICwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCgpob2xkb3V0IDwtIG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSAzLCBzdHJhdGlmeSA9IEZBTFNFKQpob2xkb3V0Q1YgPC1yZXNhbXBsZShsZWFybmVyID0gbGFzc28uMSwgdGFzayA9IHRyYWluLnRhc2subGFzc28uZmVhdC5zZWwsIAogICAgICAgICAgICAgICAgICAgICAgcmVzYW1wbGluZyA9IGhvbGRvdXQsIG1lYXN1cmVzID0gbGlzdChybXNsZSkpCgoKdHVuZWQubGFzc28uZmVhdC5zZWw8LXRyYWluKGxhc3NvLjEgLCB0cmFpbi50YXNrLmxhc3NvLmZlYXQuc2VsKQphPC1nZXRMZWFybmVyTW9kZWwodHVuZWQubGFzc28uZmVhdC5zZWwpCmxhc3NvQ29lZnMgPC1jb2VmKGEgLCBzID0gbGwpCgppbXBvcnRhbnQubGFzc28uZmVhdHVyZXM8LWRhdGEuZnJhbWUobGFzc28ucGFyID0gd2hpY2gobGFzc29Db2Vmc1ssMV0hPTApKQoKYTwtY29sbmFtZXModHJhaW4uZGF0YSkKYjwtY29sbmFtZXModChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMpKQoKbXlmdW4gPC0gZnVuY3Rpb24oc291cmNlX3ZlYywgZGVzdF92ZWMpIHsKICBwdXJycjo6bWFwX2RibChzb3VyY2VfdmVjLCB+IHdoaWNoKHN0cmluZ3I6OnN0cl9kZXRlY3QoLiwgZGVzdF92ZWMpKVsxXSkKfQoKaW1wb3J0YW50Lmxhc3NvLmZlYXR1cmVzPC1uYS5vbWl0KHVuaXF1ZShjb2xuYW1lcyh0cmFpbi5kYXRhKVtteWZ1bihiLCBhKV0pKQppbXBvcnRhbnQubGFzc28uZmVhdHVyZXM8LWFwcGVuZChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMgLCAiU2FsZVByaWNlIikKI2ltcG9ydGFudC5sYXNzby5mZWF0dXJlcwoKYmIudGVzdC5sYXNzby5mZWF0PC1wcmVkaWN0KHR1bmVkLmxhc3NvLmZlYXQuc2VsICwgbmV3ZGF0YSA9IHRlc3QuZGF0YSkkZGF0YSRyZXNwb25zZQoKbGVhcm5pbmcuY3VydmUubGFzc28uMTwtcGxvdC5sZWFybmluZy5jdXJ2ZShsYXNzby4xICwgdHJhaW4udGFzay5sYXNzby5mZWF0LnNlbCwgImxhc3NvIC0gdHJhaW4uZGF0YS5yb290IikKCmJiLnRyYWluLmxhc3NvLmZlYXQ8LXByZWRpY3QodHVuZWQubGFzc28uZmVhdC5zZWwsIG5ld2RhdGEgPSB0cmFpbi5kYXRhLnJvb3QpJGRhdGEkcmVzcG9uc2UKCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFdlIHdpbGwgYWxzbyBjcmVhdGUgYSAgKioqYmFnZ2luZyBlbGFzdGljLW5ldCBtb2RlbCoqKiB3ZSB3ZWxsPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CgpsbDwtMTIwMApzZXQuc2VlZCgxKQplbGFzdGljLm5ldDwtbWFrZUxlYXJuZXIoInJlZ3IuZ2xtbmV0IiwgaWQgPSAiZWxhc3RpYy5uZXQiLCBwYXIudmFscyA9IGxpc3QoYWxwaGEgPSAwLjUgLCBzID0gbGwpKQpiYWcubHJuLmVuZXQgPSBtYWtlQmFnZ2luZ1dyYXBwZXIoZWxhc3RpYy5uZXQgLCBidy5pdGVycyA9IDUwLCBidy5yZXBsYWNlID0gVFJVRSwgYncuZmVhdHMgPSAwLjc1KQp0cmFpbi50YXNrLmVuZXQ8LW1ha2VSZWdyVGFzayhkYXRhID0gdHJhaW4uZGF0YSx0YXJnZXQgPSAiU2FsZVByaWNlIikKCmhvbGRvdXQgPC0gbWFrZVJlc2FtcGxlRGVzYyhtZXRob2QgPSAiQ1YiLCBpdGVycyA9IDMsIHN0cmF0aWZ5ID0gRkFMU0UpCmhvbGRvdXRDViA8LSByZXNhbXBsZShsZWFybmVyID0gYmFnLmxybi5lbmV0LCB0YXNrID0gdHJhaW4udGFzay5lbmV0LCAKICAgICAgICAgICAgICAgICAgICAgIHJlc2FtcGxpbmcgPSBob2xkb3V0LCBtZWFzdXJlcyA9IGxpc3Qocm1zbGUpKQoKdHVuZWQuYmFnZ2VkLmVuZXQ8LXRyYWluKGJhZy5scm4uZW5ldCAsIHRyYWluLnRhc2suZW5ldCkKCmJiLnRyYWluLmJhZ2dlZC5lbmV0PC1wcmVkaWN0KHR1bmVkLmJhZ2dlZC5lbmV0ICwgbmV3ZGF0YSA9IHRyYWluLmRhdGEpJGRhdGEkcmVzcG9uc2UKCmJiLnRlc3QuYmFnZ2VkLmVuZXQ8LXByZWRpY3QodHVuZWQuYmFnZ2VkLmVuZXQgLCBuZXdkYXRhID0gdGVzdC5kYXRhKSRkYXRhJHJlc3BvbnNlCgpgYGAKCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSB3aWxsIGFsc28gY3JlYXRlIGEgICoqKlNWTSoqKiB3ZSB3ZWxsPC9wPgoKCmBgYHtyLCBlY2hvPUZBTFNFfQpwPC04MgpmZWF0PC1jKChhLmZpbHRlciRkYXRhICU+JSBhcnJhbmdlKGRlc2ModmFsdWUpKSkkbmFtZVsxOnBdLCAiU2FsZVByaWNlIikKc2V0LnNlZWQoMSkKc3ZtLnJlZzwtbWFrZUxlYXJuZXIoInJlZ3Iuc3ZtIiwgcGFyLnZhbHMgPSBsaXN0KGNvc3QgPSA0ICwgbnUgPSAwLjUgLHRvbGVyYW5jZSA9IDAuMDAxICkpCnRyYWluLnRhc2suc3ZtPC1tYWtlUmVnclRhc2soZGF0YSA9ICB0cmFpbi5kYXRhICU+JSBzZWxlY3QoZmVhdCkgICAsIHRhcmdldCA9ICJTYWxlUHJpY2UiKQoKaG9sZG91dCA8LSBtYWtlUmVzYW1wbGVEZXNjKG1ldGhvZCA9ICJDViIsIGl0ZXJzID0gMywgc3RyYXRpZnkgPSBGQUxTRSkKaG9sZG91dENWIDwtIHJlc2FtcGxlKGxlYXJuZXIgPSBzdm0ucmVnLCB0YXNrID0gdHJhaW4udGFzay5zdm0sIAogICAgICAgICAgICAgICAgICAgICAgcmVzYW1wbGluZyA9IGhvbGRvdXQsIG1lYXN1cmVzID0gbGlzdChybXNsZSkpCgp0dW5lZC5zdm08LXRyYWluKHN2bS5yZWcgLCB0cmFpbi50YXNrLnN2bSkKCmxlYXJuaW5nLmN1cnZlLnN2bS4xPC1wbG90LmxlYXJuaW5nLmN1cnZlKHN2bS5yZWcgLCB0cmFpbi50YXNrLnN2bSAsICJzdm0uMSAtIHRyYWluLmRhdGEiKQoKc3ZtPC1lMTA3MTo6c3ZtKFNhbGVQcmljZSB+IC4gLCBkYXRhID0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBrZXJuZWwgPSAicmFkaWFsIiwgCiAgICAgICAgICAgICAgICB0b2xlcmFuY2UgPSAwLjAwMSx0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwgbnUgPSAwLjUsIGNvc3QgPSA0KQpiYi50ZXN0LnN2bTwtcHJlZGljdChzdm0gLCBuZXdkYXRhID0gdGVzdC5kYXRhJT4lIHNlbGVjdChmZWF0LCAtU2FsZVByaWNlKSkKCgpiYi50cmFpbi5zdm08LXByZWRpY3Qoc3ZtICwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSkKCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFdlIHdpbGwgYWxzbyBjcmVhdGUgYSAgKioqU1ZNIDIqKiogd2Ugd2VsbC4gKioqVGhlIHdhcm5pbmcgY2FuIGJlIHNhZmVseSBpZ25vcmVkKioqPC9wPgoKYGBge3IsIGVjaG89RkFMU0V9CgpzZXQuc2VlZCgxKQpzdm0ucmVnLjI8LW1ha2VMZWFybmVyKCJyZWdyLnN2bSIsIHBhci52YWxzID0gbGlzdChjb3N0ID0gNSwgdG9sZXJhbmNlID0gMC4wMDAwMDEgLCBudSA9IDAuNSwgc2NhbGUgPSBUUlVFKSkKdHJhaW4udGFzay5zdm08LW1ha2VSZWdyVGFzayhkYXRhID0gIHRyYWluLmRhdGEgJT4lIHNlbGVjdChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKCgpob2xkb3V0IDwtIG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSAzLCBzdHJhdGlmeSA9IEZBTFNFKQpob2xkb3V0Q1YgPC0gcmVzYW1wbGUobGVhcm5lciA9IHN2bS5yZWcuMiwgdGFzayA9IHRyYWluLnRhc2suc3ZtLCAKICAgICAgICAgICAgICAgICAgICAgIHJlc2FtcGxpbmcgPSBob2xkb3V0LCBtZWFzdXJlcyA9IGxpc3Qocm1zbGUpKQoKbGVhcm5pbmcuY3VydmUuc3ZtLjI8LXBsb3QubGVhcm5pbmcuY3VydmUoc3ZtLnJlZy4yICwgdHJhaW4udGFzay5zdm0gLCAic3ZtLjIgLSB0cmFpbi5kYXRhICU+JSBpbXBvcnRhbnQgbGFzc28gZmVhdHVyZXMiKQoKc3ZtLjI8LWUxMDcxOjpzdm0oU2FsZVByaWNlIH4gLiAsIGRhdGEgPSB0cmFpbi5kYXRhICU+JSBzZWxlY3QoaW1wb3J0YW50Lmxhc3NvLmZlYXR1cmVzKSAsIAogICAgICAgICAgICAgICAgICBrZXJuZWwgPSAicmFkaWFsIiwgdG9sZXJhbmNlID0gMC4wMDAwMDEsdHlwZSA9ICJlcHMtcmVncmVzc2lvbiIsIG51ID0gMC41LCBjb3N0ID0gNSkKCmJiLnRlc3Quc3ZtMjwtcHJlZGljdChzdm0uMiwgbmV3ZGF0YSA9IHRlc3QuZGF0YSAlPiUgc2VsZWN0KGltcG9ydGFudC5sYXNzby5mZWF0dXJlcykpCmJiLnRyYWluLnN2bTI8LXByZWRpY3Qoc3ZtLjIsIG5ld2RhdGEgPSB0cmFpbi5kYXRhICU+JSBzZWxlY3QoaW1wb3J0YW50Lmxhc3NvLmZlYXR1cmVzKSkKCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBhbHNvIGNyZWF0ZSBhICAqKipLU1ZNKioqIHdlIHdlbGw8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KCnA8LTcyCmZlYXQ8LWMoKGEuZmlsdGVyJGRhdGEgJT4lIGFycmFuZ2UoZGVzYyh2YWx1ZSkpKSRuYW1lWzE6cF0sICJTYWxlUHJpY2UiKQoKc2V0LnNlZWQoMikKbGlicmFyeShrZXJubGFiKQprc3ZtLnJlZzwtbWFrZUxlYXJuZXIoInJlZ3Iua3N2bSIsIHBhci52YWxzID0gbGlzdChDID0gMiwgZXBzaWxvbiA9IDAuMDAxLCBudSA9IDAuMiApKQp0cmFpbi50YXNrLmtzdm08LW1ha2VSZWdyVGFzayhkYXRhID0gIHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQgPSAiU2FsZVByaWNlIikKCmhvbGRvdXQgPC0gbWFrZVJlc2FtcGxlRGVzYyhtZXRob2QgPSAiQ1YiLCBpdGVycyA9IDMsIHN0cmF0aWZ5ID0gRkFMU0UpCmhvbGRvdXRDViA8LSByZXNhbXBsZShsZWFybmVyID0ga3N2bS5yZWcsIHRhc2sgPSB0cmFpbi50YXNrLmtzdm0sIAogICAgICAgICAgICAgICAgICAgICAgcmVzYW1wbGluZyA9IGhvbGRvdXQsIG1lYXN1cmVzID0gbGlzdChybXNsZSkpCgprc3ZtPC1rZXJubGFiOjprc3ZtKFNhbGVQcmljZSB+LiwgZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSAsQyA9IDIsIGVwc2lsb24gPSAwLjAwMDEsIG51ID0wLjIgKQoKbGVhcm5pbmcuY3VydmUua3N2bTwtcGxvdC5sZWFybmluZy5jdXJ2ZShrc3ZtLnJlZyAsIHRyYWluLnRhc2sua3N2bSwgImtzdm0gLSB0cmFpbi5kYXRhICU+JSBmZWF0IikKCmJiLnRyYWluLmtzdm08LXByZWRpY3Qoa3N2bSwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSkKCmJiLnRlc3Qua3N2bTwtcHJlZGljdChrc3ZtLCBuZXdkYXRhID0gdGVzdC5kYXRhICU+JSBzZWxlY3QoZmVhdCkpCgpgYGAKCgoKPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugd2lsbCBhbHNvIGNyZWF0ZSBhICAqKipYR0IqKiogd2Ugd2VsbDwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQoKcDwtNjAKZmVhdDwtYygoYS5maWx0ZXIkZGF0YSAlPiUgYXJyYW5nZShkZXNjKHZhbHVlKSkpJG5hbWVbMTpwXSwgIlNhbGVQcmljZSIpCgpzZXQuc2VlZCgyKQp4Z2I8LW1ha2VMZWFybmVyKCJyZWdyLnhnYm9vc3QiLCBwYXIudmFscyA9IGxpc3QoZXRhID0gMSwgbWF4X2RlcHRoID0gMTAsIGV2YWxfbWV0cmljID0gJ3Jtc2xlJywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fY2hpbGRfd2VpZ2h0ID0gNSwgZ2FtbWEgPSAxMCkpCnhnYi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IG11dGF0ZV9hbGwodHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBhcy5udW1lcmljKSwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCgpob2xkb3V0IDwtIG1ha2VSZXNhbXBsZURlc2MobWV0aG9kID0gIkNWIiwgaXRlcnMgPSAzLCBzdHJhdGlmeSA9IEZBTFNFLCBwcmVkaWN0ID0gImJvdGgiKQpob2xkb3V0Q1YgPC0gcmVzYW1wbGUobGVhcm5lciA9IHhnYiwgdGFzayA9IHhnYi50YXNrLCAKICAgICAgICAgICAgICAgICAgICAgIHJlc2FtcGxpbmcgPSBob2xkb3V0LCBtZWFzdXJlcyA9IGxpc3Qocm1zbGUpKQoKbGVhcm5pbmcuY3VydmUueGdiPC1wbG90LmxlYXJuaW5nLmN1cnZlKHhnYiAsIHhnYi50YXNrLCAieGdiIC0gdHJhaW4uZGF0YSAlPiUgZmVhdCIpCgp0dW5lZC54Z2I8LXRyYWluKHhnYiwgeGdiLnRhc2spCgpiYi50cmFpbi54Z2I8LXByZWRpY3QodHVuZWQueGdiICwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSkKCmJiLnRlc3QueGdiPC1wcmVkaWN0KHR1bmVkLnhnYiAsIG5ld2RhdGEgPSB0ZXN0LmRhdGEgJT4lIHNlbGVjdChmZWF0KSkkZGF0YSRyZXNwb25zZQoKYGBgCgo8cCBzdHlsZT0iZm9udC1zaXplOjMwcHgiPiAqKipHTE1CT09TVCoqKjwvcD4KCgpgYGB7ciwgZWNobz1GQUxTRX0KcDwtODAKZmVhdDwtYygoYS5maWx0ZXIkZGF0YSAlPiUgYXJyYW5nZShkZXNjKHZhbHVlKSkpJG5hbWVbMTpwXSwgIlNhbGVQcmljZSIpCgpnbG0uYm9vc3Q8LW1ha2VMZWFybmVyKCJyZWdyLmdsbWJvb3N0IiwgcGFyLnZhbHMgPSBsaXN0KGNlbnRlciA9IEZBTFNFLCBtc3RvcCA9IDEwMDAwLCBudSA9IDEpKQpnbG0uYm9vc3QudGFzazwtbWFrZVJlZ3JUYXNrKGRhdGEgPSBtdXRhdGVfYWxsKHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSwgYXMubnVtZXJpYyksIHRhcmdldCA9ICJTYWxlUHJpY2UiKQoKdHVuZWQuZ2xtLmJvb3N0PC10cmFpbihnbG0uYm9vc3QsIGdsbS5ib29zdC50YXNrKQoKaG9sZG91dCA8LSBtYWtlUmVzYW1wbGVEZXNjKG1ldGhvZCA9ICJDViIsIGl0ZXJzID0gNSwgc3RyYXRpZnkgPSBGQUxTRSwgcHJlZGljdCA9ICJib3RoIikKaG9sZG91dENWIDwtIHJlc2FtcGxlKGxlYXJuZXIgPSBnbG0uYm9vc3QsIHRhc2sgPSBnbG0uYm9vc3QudGFzaywgCiAgICAgICAgICAgICAgICAgICAgICByZXNhbXBsaW5nID0gaG9sZG91dCwgbWVhc3VyZXMgPSBsaXN0KHJtc2xlKSkKCmxlYXJuaW5nLmN1cnZlLmdsbS5ib29zdDwtcGxvdC5sZWFybmluZy5jdXJ2ZSh4Z2IgLCB4Z2IudGFzaywgImdsbS5ib29zdCAtIHRyYWluLmRhdGEgJT4lIGZlYXQiKQoKYmIudHJhaW4uZ2xtLmJvb3N0PC1wcmVkaWN0KHR1bmVkLmdsbS5ib29zdCwgbmV3ZGF0YSA9IG11dGF0ZV9hbGwodHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBhcy5udW1lcmljKSkkZGF0YSRyZXNwb25zZQoKYmIudGVzdC5nbG0uYm9vc3Q8LXByZWRpY3QodHVuZWQuZ2xtLmJvb3N0LCBuZXdkYXRhID0gbXV0YXRlX2FsbCh0ZXN0LmRhdGEgJT4lIHNlbGVjdChmZWF0KSwgYXMubnVtZXJpYykpJGRhdGEkcmVzcG9uc2UKCmBgYAoKCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSB3aWxsIGFsc28gY3JlYXRlIGEgICoqKlNWTSAzKioqIHdlIHdlbGw8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0KIyBObyBMZWFybmluZyBjdXJ2ZSBvciBDViB3aWxsIGJlIG1hZGUgc2luY2UgdGhleSB3aWxsIGJlIHNpbWlsYXIgYXMgdGhlIG90aGVyIFNWTXMKCgpzdm0ucmVnLnNjYWxlPC1tYWtlTGVhcm5lcigicmVnci5zdm0iICwgcGFyLnZhbHMgPSBsaXN0KG51ID0gMC41LCBjb3N0ID0gMyAsIHRvbGVyYW5jZSA9IDAuMDAxKSkKcDwtODIKCmZlYXQ8LWMoKGEuZmlsdGVyJGRhdGEgJT4lIGFycmFuZ2UoZGVzYyh2YWx1ZSkpKSRuYW1lWzE6cF0sICJTYWxlUHJpY2UiKQoKc3ZtLnNjYWxlPC1lMTA3MTo6c3ZtKFNhbGVQcmljZSB+IC4gLCBkYXRhID0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCBrZXJuZWwgPSAicmFkaWFsIiwgCiAgICAgICAgICAgICAgICB0b2xlcmFuY2UgPSAwLjAwMSx0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwgbnUgPSAwLjUsIGNvc3QgPSAzKQoKYmIudHJhaW4uc3ZtLnNjYWxlPC1wcmVkaWN0KHN2bS5zY2FsZSwgbmV3ZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0LCAtU2FsZVByaWNlKSkKYmIudGVzdC5zdm0uc2NhbGU8LXByZWRpY3Qoc3ZtLnNjYWxlLCBuZXdkYXRhID0gdGVzdC5kYXRhICU+JSBzZWxlY3QoZmVhdCwgLVNhbGVQcmljZSkpCgpgYGAKCgoKYGBge3IsIGVjaG89RkFMU0V9CiMgU3RhY2tlZCBtb2RlbCBkYXRhZnJhbWUKCnNldC5zZWVkKDEpCnN0YWNrZWQuZGF0YS50cmFpbjwtZGF0YS5mcmFtZShiYi50cmFpbi5sYXNzby5mZWF0LCBiYi50cmFpbi5iYWdnZWQuZW5ldCwgYmIudHJhaW4uc3ZtLCBiYi50cmFpbi5zdm0yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJiLnRyYWluLmtzdm1bLDFdICwgYmIudHJhaW4ueGdiJGRhdGEkcmVzcG9uc2UgLCBiYi50cmFpbi5nbG0uYm9vc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYi50cmFpbi5zdm0uc2NhbGUgLCB0cmFpbi5kYXRhJFNhbGVQcmljZSkKY29sbmFtZXMoc3RhY2tlZC5kYXRhLnRyYWluKTwtYygibGFzc28iLCAiYmFnZ2VkLmVuZXQiICwgInN2bSIgLCAic3ZtLjIiICwgImtzdm0iICwgInhnYiIgLCAiZ2xtLmJvb3N0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3ZtLnNjYWxlIiwiU2FsZVByaWNlIikKCnN0YWNrZWQuZGF0YS50ZXN0PC1kYXRhLmZyYW1lKGJiLnRlc3QubGFzc28uZmVhdCAsIGJiLnRlc3QuYmFnZ2VkLmVuZXQgLCBiYi50ZXN0LnN2bSAsIGJiLnRlc3Quc3ZtMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmIudGVzdC5rc3ZtWywxXSAsIGJiLnRlc3QueGdiLCBiYi50ZXN0LmdsbS5ib29zdCwgYmIudGVzdC5zdm0uc2NhbGUpCmNvbG5hbWVzKHN0YWNrZWQuZGF0YS50ZXN0KTwtYygibGFzc28iLCAiYmFnZ2VkLmVuZXQiICwgInN2bSIgLCAic3ZtLjIiICwgImtzdm0iICwgInhnYiIgLCAiZ2xtLmJvb3N0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdm0uc2NhbGUiKQpgYGAKCgoKPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gU3RhY2tpbmc6IExhc3NvIE1vZGVsPC9wPgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSB3aWxsIGNyZWF0ZSBhICoqKkxBU1NPKioqIG1vZGVsIGFzIG91ciAqKipzdXBlcmxlYXJuZXIqKio8L3A+CjxwIHN0eWxlPSJmb250LXNpemU6MTVweCI+IFRoZSBiYXNlIGxlYXJuZXJzIHdlcmUgKioqc3ZtLCBhbmQga3N2bSoqKiBhbmQgSSB1c2VkIGEgKioqbGFzc28gbW9kZWwqKiogYXMgb3VyIHN1cGVyLWxlYXJuZXI8L3A+CgpgYGB7ciwgZWNobz1GQUxTRX0Kc2V0LnNlZWQoMSkKYTwtYygic3ZtIiAsICJrc3ZtIiAsICAiU2FsZVByaWNlIikKc3RhY2tlZC5sYXNzbzwtbWFrZUxlYXJuZXIoInJlZ3IuZ2xtbmV0IiAsIGFscGhhID0gMSwgcGFyLnZhbHMgPSBsaXN0KHM9OTAwKSkKc3RhY2tlZC50cmFpbi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IHN0YWNrZWQuZGF0YS50cmFpbiAlPiUgc2VsZWN0KGEpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKdHVuZWQuc3RhY2tlZC5sYXNzbzwtdHJhaW4oc3RhY2tlZC5sYXNzbyAsIHN0YWNrZWQudHJhaW4udGFzaykKCmhvbGRvdXQgPC0gbWFrZVJlc2FtcGxlRGVzYyhtZXRob2QgPSAiQ1YiLCBpdGVycyA9IDUsIHN0cmF0aWZ5ID0gRkFMU0UsIHByZWRpY3QgPSAiYm90aCIpCmhvbGRvdXRDViA8LSByZXNhbXBsZShsZWFybmVyID0gc3RhY2tlZC5sYXNzbywgdGFzayA9IHN0YWNrZWQudHJhaW4udGFzaywgCiAgICAgICAgICAgICAgICAgICAgICByZXNhbXBsaW5nID0gaG9sZG91dCwgbWVhc3VyZXMgPSBsaXN0KHJtc2xlKSkKCiMgc2VsZWN0KGFbLWxlbmd0aChhKV0pCgpiYi50cmFpbi5zdGFja2VkPC1wcmVkaWN0KHR1bmVkLnN0YWNrZWQubGFzc28gLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gc3RhY2tlZC5kYXRhLnRyYWluICU+JSBzZWxlY3QoYVstbGVuZ3RoKGEpXSkpJGRhdGEkcmVzcG9uc2UKYmIudGVzdC5zdGFja2VkPC1wcmVkaWN0KHR1bmVkLnN0YWNrZWQubGFzc28gLCBuZXdkYXRhID0gc3RhY2tlZC5kYXRhLnRlc3QgJT4lIHNlbGVjdChhWy1sZW5ndGgoYSldKSkkZGF0YSRyZXNwb25zZQoKYGBgCgpgYGB7cn0Kc3RhY2tlZC5kYXRhLnRyYWluPC1hZGRfY29sdW1uKHN0YWNrZWQuZGF0YS50cmFpbiAsIHN0YWNrZWQubW9kZWwgPSBiYi50cmFpbi5zdGFja2VkKQpzdGFja2VkLmRhdGEudGVzdDwtYWRkX2NvbHVtbihzdGFja2VkLmRhdGEudGVzdCAsIHN0YWNrZWQubW9kZWwgPSBiYi50ZXN0LnN0YWNrZWQpCmBgYAoKCjxwIHN0eWxlPSJmb250LXNpemU6MzBweCI+IFN0YWNraW5nOiBCZW5jaG1hcmtpbmc8L3A+CgpgYGB7cn0Kc2V0LnNlZWQoMikKc3ZtLnJlZy5zY2FsZTwtbWFrZUxlYXJuZXIoInJlZ3Iuc3ZtIiAsIHBhci52YWxzID0gbGlzdChudSA9IDAuNSwgY29zdCA9IDMgLCB0b2xlcmFuY2UgPSAwLjAwMSksIGlkID0gInN2bS5zY2FsZSIpCmtzdm0ucmVnPC1tYWtlTGVhcm5lcigicmVnci5rc3ZtIiwgcGFyLnZhbHMgPSBsaXN0KEMgPSAyLCBlcHNpbG9uID0gMC4wMDEsIG51ID0gMC4yKSwgaWQgPSAia3N2bSIpCnN2bS5yZWcuMjwtbWFrZUxlYXJuZXIoInJlZ3Iuc3ZtIiwgcGFyLnZhbHMgPSBsaXN0KGNvc3QgPSA0LCB0b2xlcmFuY2UgPSAwLjAwNSAsIG51ID0gMC41LCBzY2FsZSA9IFRSVUUpICwgCiAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAic3ZtLnJlZzIiKQpzdm0ucmVnPC1tYWtlTGVhcm5lcigicmVnci5zdm0iLCBwYXIudmFscyA9IGxpc3QoY29zdCA9IDQgLCBudSA9IDAuNSAsdG9sZXJhbmNlID0gMC4wMDEgKSwgaWQgPSAic3ZtIikKCgpscm5zID0gbGlzdChzdm0ucmVnLCBzdm0ucmVnLjIsIHN2bS5yZWcuc2NhbGUpCnRyYWluLnRhc2s8LW1ha2VSZWdyVGFzayhkYXRhID0gdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKcmVwY3YgPSBtYWtlUmVzYW1wbGVEZXNjKCJSZXBDViIgLCBmb2xkcyA9IDMsIHJlcCA9IDUpCgpiMTwtYmVuY2htYXJrKGxybnMsIHRyYWluLnRhc2ssIHJlcGN2LCBtb2RlbHMgPSBUUlVFICwgbWVhc3VyZXMgPSBybXNsZSwgc2hvdy5pbmZvID0gRkFMU0UpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgxKQpscm5zID0gbGlzdChzdm0ucmVnLnNjYWxlLCBrc3ZtLnJlZywgc3ZtLnJlZy4yLCBzdm0ucmVnLCB4Z2IsIGdsbS5ib29zdCwgbGFzc28uMSwgZWxhc3RpYy5uZXQpCnN0YWNrZWQubGVhcm5lci5tb2RlbC5sYXNzbzwtbWFrZVN0YWNrZWRMZWFybmVyKGJhc2UubGVhcm5lcnMgPSBscm5zLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VwZXIubGVhcm5lciA9IG1ha2VMZWFybmVyKCJyZWdyLmdsbW5ldCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAxLCBwYXIudmFscyA9IGxpc3Qocz04MDApKSkKCmxybnMgPSBsaXN0KHhnYiwgZ2xtLmJvb3N0LCBzdGFja2VkLmxlYXJuZXIubW9kZWwubGFzc28pCnRyYWluLnRhc2s8LW1ha2VSZWdyVGFzayhkYXRhID0gbXV0YXRlX2FsbCh0cmFpbi5kYXRhICU+JSBzZWxlY3QoZmVhdCksIGFzLm51bWVyaWMpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKcmVwY3YgPSBtYWtlUmVzYW1wbGVEZXNjKCJSZXBDViIgLCBmb2xkcyA9IDMsIHJlcCA9IDUpCgpiMjwtYmVuY2htYXJrKGxybnMsIHRyYWluLnRhc2ssIHJlcGN2LCBtb2RlbHMgPSBUUlVFICwgbWVhc3VyZXMgPSBybXNsZSwgc2hvdy5pbmZvID0gRkFMU0UpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgyKQpscm5zID0gbGlzdChsYXNzby4xKQp0cmFpbi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IHRyYWluLmRhdGEucm9vdCAlPiUgc2VsZWN0KGZlYXQpLCB0YXJnZXQgPSAiU2FsZVByaWNlIikKcmVwY3YgPSBtYWtlUmVzYW1wbGVEZXNjKCJSZXBDViIgLCBmb2xkcyA9IDMsIHJlcCA9IDUpCgpiMzwtYmVuY2htYXJrKGxybnMsIHRyYWluLnRhc2ssIHJlcGN2LCBtb2RlbHMgPSBUUlVFICwgbWVhc3VyZXMgPSBybXNsZSwgc2hvdy5pbmZvID0gRkFMU0UpCmBgYAoKCmBgYHtyLCBlY2hvPUZBTFNFfQpzZXQuc2VlZCgxKQpscm5zID0gbGlzdChlbGFzdGljLm5ldCkKdHJhaW4udGFzazwtbWFrZVJlZ3JUYXNrKGRhdGEgPSB0cmFpbi5kYXRhICwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCnJlcGN2ID0gbWFrZVJlc2FtcGxlRGVzYygiUmVwQ1YiICwgZm9sZHMgPSAzLCByZXAgPSA1KQoKYjQ8LWJlbmNobWFyayhscm5zLCB0cmFpbi50YXNrLCByZXBjdiwgbW9kZWxzID0gVFJVRSAsIG1lYXN1cmVzID0gcm1zbGUsIHNob3cuaW5mbyA9IEZBTFNFKQoKbHJucyA9IGxpc3Qoc3ZtLnJlZy4yKQp0cmFpbi50YXNrPC1tYWtlUmVnclRhc2soZGF0YSA9IHRyYWluLmRhdGEgJT4lIHNlbGVjdChpbXBvcnRhbnQubGFzc28uZmVhdHVyZXMpICwgdGFyZ2V0ID0gIlNhbGVQcmljZSIpCnJlcGN2ID0gbWFrZVJlc2FtcGxlRGVzYygiUmVwQ1YiICwgZm9sZHMgPSAzLCByZXAgPSA1KQoKYjU8LWJlbmNobWFyayhscm5zLCB0cmFpbi50YXNrLCByZXBjdiwgbW9kZWxzID0gVFJVRSAsIG1lYXN1cmVzID0gcm1zbGUsIHNob3cuaW5mbyA9IEZBTFNFKQpgYGAKCgpgYGB7ciwgZWNobz1GQUxTRX0KYjEuZGF0YTwtZ2V0Qk1SUGVyZm9ybWFuY2VzKGIxKSRgdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGZlYXQpYApiMi5kYXRhPC1nZXRCTVJQZXJmb3JtYW5jZXMoYjIpJGBtdXRhdGVfYWxsKHRyYWluLmRhdGEgJT4lIHNlbGVjdChmZWF0KSwgYXMubnVtZXJpYylgCmIzLmRhdGE8LWdldEJNUlBlcmZvcm1hbmNlcyhiMykkYHRyYWluLmRhdGEucm9vdCAlPiUgc2VsZWN0KGZlYXQpYApiNC5kYXRhPC1nZXRCTVJQZXJmb3JtYW5jZXMoYjQpJHRyYWluLmRhdGEKYjUuZGF0YTwtZ2V0Qk1SUGVyZm9ybWFuY2VzKGI1KSRgdHJhaW4uZGF0YSAlPiUgc2VsZWN0KGltcG9ydGFudC5sYXNzby5mZWF0dXJlcylgCgpiZW5jaC5tYXJrLmRhdGE8LWRhdGEuZnJhbWUoc3ZtMS5mZWF0ID0gYjEuZGF0YSRzdm0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdm0yLmZlYXQgPSBiMS5kYXRhJHN2bS5yZWcyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ZtMy5mZWF0ICA9IGIxLmRhdGEkc3ZtLnNjYWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgeGdib29zdC5mZWF0ID0gYjIuZGF0YSRyZWdyLnhnYm9vc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnbG1ib29zdC5mZWF0ID0gYjIuZGF0YSRyZWdyLmdsbWJvb3N0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzc28ubW9kZWwuc3RhY2tlZC5mZWF0ID0gYjIuZGF0YSRzdGFjaywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3NvLmZlYXQgPWIzLmRhdGEkbGFzc28sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZS5uZXQuYWxsLnZhciA9IGI0LmRhdGEkZWxhc3RpYy5uZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdm00Lmxhc3NvLmZlYXR1cmVzID0gYjUuZGF0YSRzdm0ucmVnMikKCmBgYAoKPHAgc3R5bGU9ImZvbnQtc2l6ZTozMHB4Ij4gU3RhY2tpbmc6IEJlbmNobWFya2luZyBib3hwbG90czwvcD4KPHAgc3R5bGU9ImZvbnQtc2l6ZToxNXB4Ij4gV2Ugc2VlIHRoYXQgKioqYmVuY2htYXJraW5nKioqIGF2ZXJhZ2VkIHRoZSByZXN1bHRzIG9mIGFsbCB0aGUgTUwgbW9kZWxzPC9wPgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBXZSBjYW4gY3JlYXRlIGFub3RoZXIgKioqc3RhY2tpbmcgbW9kZWwqKiogdG8gaW5jcmVhc2UgcGVyZm9ybWFuY2UsIG9yIHdlIGNhbiBtYW51YWxseSBhZGp1c3QgdGhlIHdlaWdodHMgdG8gc2VlIGhvdyB3ZWxsIHdlIGRvIGl0LjwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpzLmRmPC1yZXNoYXBlMjo6bWVsdChiZW5jaC5tYXJrLmRhdGEgJT4lIHNlbGVjdF9pZihpcy5kb3VibGUpKQpnZ3Bsb3QoZGF0YSA9IHMuZGYsIGFlcyh4ID0gdmFyaWFibGUgLCB5ID0gdmFsdWUpKSArIGdlb21fYm94cGxvdCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSArIGdndGl0bGUoIkJveHBsb3Qgb2YgZWFjaCBNTCBtb2RlbHMiKQpgYGAKCjxwIHN0eWxlPSJmb250LXNpemU6MzBweCI+IFN0YWNraW5nOiBNYW51ZWwgd2VpZ2h0cyBiYXNlZCBvbiBSTVNMRTwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpybXNsZS5sb3NzPC1jKCkKZm9yKGkgaW4gMTo1KXsKICBzZXQuc2VlZChpKQogIGxpYnJhcnkoc3BsaXRUb29scykKICBteS5kYXRhLnNwbGl0PC1zdGFja2VkLmRhdGEudHJhaW4gJT4lIHNlbGVjdChjKCJzdm0iLCJrc3ZtIiwgICJnbG0uYm9vc3QiLCAic3RhY2tlZC5tb2RlbCIsICJTYWxlUHJpY2UiKSkKICBpbmRleDwtcGFydGl0aW9uKHN0YWNrZWQuZGF0YS50cmFpbiRTYWxlUHJpY2UgLCBwID0gYyh0cmFpbiA9IDAuMywgdmFsaWQgPSAwLjMsIHRlc3QgPSAwLjMpKQogIGFhPC1teS5kYXRhLnNwbGl0W2luZGV4JHRyYWluLF0KICBiYjwtbXkuZGF0YS5zcGxpdFtpbmRleCR2YWxpZCxdCiAgY2M8LW15LmRhdGEuc3BsaXRbaW5kZXgkdGVzdCxdCgogIGE8LTAuNgogIGI8LTAKICBjPC0wLjIKICBkPC0wLjcKICAKCiAgcHAuMTwtYXBwbHkoYWEgJT4lIHNlbGVjdCgtU2FsZVByaWNlKSAsIDEsIAogICAgICAgICAgICBmdW5jdGlvbih4KXtyZXR1cm4od2VpZ2h0ZWQubWVhbih4LCB3ID0gYyhhLGIsYyxkKSkpfSkKCiAgcHAuMjwtYXBwbHkoYmIgJT4lIHNlbGVjdCgtU2FsZVByaWNlKSAsIDEsIAogICAgICAgICAgICBmdW5jdGlvbih4KXtyZXR1cm4od2VpZ2h0ZWQubWVhbih4LCB3ID0gYyhhLGIsYyxkKSkpfSkKCiAgcHAuMzwtYXBwbHkoY2MgJT4lIHNlbGVjdCgtU2FsZVByaWNlKSAsIDEsIAogICAgICAgICAgICBmdW5jdGlvbih4KXtyZXR1cm4od2VpZ2h0ZWQubWVhbih4LCB3ID0gYyhhLGIsYyxkKSkpfSkKCiAgcm1zbGUuMTwtc3FydChtZWFuKChsb2cocHAuMSArMSkgLSBsb2coYWEkU2FsZVByaWNlKSleMikpCiAgcm1zbGUuMjwtc3FydChtZWFuKChsb2cocHAuMiArMSkgLSBsb2coYmIkU2FsZVByaWNlKSleMikpCiAgcm1zbGUuMzwtc3FydChtZWFuKChsb2cocHAuMyArMSkgLSBsb2coY2MkU2FsZVByaWNlKSleMikpCgogIHJtc2xlLmxvc3M8LWMocm1zbGUubG9zcyAscm1zbGUuMSAsIHJtc2xlLjIgLCBybXNsZS4zICkKfQpgYGAKCgo8cCBzdHlsZT0iZm9udC1zaXplOjE1cHgiPiBJdCBpcyB2ZXJ5IGV2aWRlbnQgdGhhdCB0aGUgY3VzdG9tIHdlaWdodHMgd2UgaGF2ZSBjcmVhdGVkIGdhdmUgdXMgdGhlIGdyZWF0ZXN0IG92ZXJhbGwgcGVyZm9ybWFuY2UuIFdlIHdpbGwgdXNlIHRoZXNlIGN1c3RvbSB3ZWlnaHRzIGZvciByZXByZXNlbnQgb3VyIHRlc3QgZGF0YSBhcyB3ZWxsLjwvcD4KCmBgYHtyLCBlY2hvPUZBTFNFfQpiZW5jaC5tYXJrLmRhdGE8LWFkZF9jb2x1bW4oYmVuY2gubWFyay5kYXRhICwgY3VzdG9tLndlaWdodHMgPXJtc2xlLmxvc3MgKQpzLmRmPC1yZXNoYXBlMjo6bWVsdChiZW5jaC5tYXJrLmRhdGEgJT4lIHNlbGVjdF9pZihpcy5kb3VibGUpKQpnZ3Bsb3QoZGF0YSA9IHMuZGYsIGFlcyh4ID0gdmFyaWFibGUgLCB5ID0gdmFsdWUpKSArIGdlb21fYm94cGxvdCgpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKQpgYGAKCgo8cD5UaGUgZmluYWwgcHJlZGljdGlvbnM8L3A+CmBgYHtyLCBlY2hvPVRSVUV9Cgp6PC1zdGFja2VkLmRhdGEudGVzdCAlPiUgc2VsZWN0KGMoInN2bSIsImtzdm0iLCAgImdsbS5ib29zdCIsICJzdGFja2VkLm1vZGVsIikpCgphPC0wLjYKYjwtMApjPC0wLjIKZDwtMC43CnBwPC1hcHBseSh6ICwgMSwgZnVuY3Rpb24oeCl7cmV0dXJuKHdlaWdodGVkLm1lYW4oeCwgdyA9IGMoYSxiLGMsZCkpKX0pCnByaW50KGhlYWQocHApKQpgYGAKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK